package de.breitbeil.samsyncro.lib.connection;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * Utility class to send AT commands (also known as Hayes commands)
 * to a Samsung mobile and receive information like vendor or model
 * name of the mobile. 
 * <p>
 * On some Samsung mobiles you can start the obex server with
 * a non-standard AT command (see {@link #startObexServer()}).
 * <p>
 * 
 * @author Dominik Breitbeil
 *
 */
public class ATConnection {
    
    private final Log log = LogFactory.getLog(ATConnection.class);
    
    private InputStream inp = null;
    private OutputStream outp = null;
    
    private final byte[] cmd_atz = "ATZ\r\n".getBytes();
    private final byte[] cmd_gmi = "AT+GMI\r\n".getBytes();
    private final byte[] cmd_gmm = "AT+GMM\r\n".getBytes();
    private final byte[] cmd_startobex = "AT+SYNCML=MOBEXSTART\r\n".getBytes();
    
    public ATConnection(OutputStream outp, InputStream inp) {
        this.outp = outp;
        this.inp = inp;
    }
    
    /**
     * Sends ATZ and returns the response which is in success OK.
     * 
     * @return "OK" if the command was successful.
     */
    public String atz() {
        writeBytes(cmd_atz);
        
        try { 
            Thread.sleep(100); 
        } catch (InterruptedException ignore) {}
        
        byte[] answer = read();
        return getAnswer(answer);
    }
    
    /**
     * Returns the vendor of the mobile.
     * 
     * @return vendor of the mobile.
     */
    public String getVendor() {
        writeBytes(cmd_gmi);
        
        try { 
            Thread.sleep(100); 
        } catch (InterruptedException ignore) {}
        
        byte[] answer = read();
        return getAnswer(answer);        
    }
    
    /**
     * Sends AT+GMM and returns the model name of the mobile.
     * 
     * @return the model name of the mobile. 
     */
    public String getModel() {
        writeBytes(cmd_gmm);
        
        try { 
            Thread.sleep(100); 
        } catch (InterruptedException ignore) {}
        
        byte[] answer = read();
        return getAnswer(answer);
    }
    
    /**
     * Starts the obex server. The AT command to start the server 
     * is AT+SYNCML=MOBEXSTART. 
     * <p>
     * Beware that the mobile must be put to the home screen and it must be active (screen
     * must be on). Otherwise the obex server will not start and you will receive the
     * error 2003.
     * 
     * @throws MObexException if the start of the obex server fails.
     */
    public void startObexServer() throws MObexException {
        writeBytes(cmd_startobex);
        
        try { 
            Thread.sleep(100); 
        } catch (InterruptedException ignore) {}
        
        byte[] answer = read();
        String s = new String(answer);
        
        if(!s.contains("OK")) {
            throw new MObexException("Can't start obex server. Answer was "+new String(answer));
        }
    }
    
    /**
     * Tries to get an answer from the mobile.
     * 
     * @return answer in bytes.
     */
    public byte[] read() {
        byte[] buffer = new byte[1024];
        int read = readBytes(buffer, 1024, 0);
        return Arrays.copyOfRange(buffer, 0, read);
    }
    
    /**
     * Tries to get an answer from the mobile.
     * 
     * @param b buffer in which the answer will be written.
     * @param len maximum number of bytes to read. 
     * @param offset start index
     * @return number of bytes read.
     */
    public int readBytes(byte[] b, int len, int offset) {
        log.debug("reading...");
        if(inp == null) {
            return -1;
        }

        int bytes_read = 0;
        try {
            int newbytes = inp.read(b, offset, len);
            bytes_read += newbytes;
            
            log.debug("read "+new String(b, offset, len));
        } catch (IOException e) { 
            e.printStackTrace(); 
        }
        
        return bytes_read;
    }

    /**
     * Sends the bytes to the mobile.
     * 
     * @param b
     * @return number of bytes written.
     */
    public int writeBytes(byte[] b) {
        return writeBytes(b, b.length, 0);
    }
    
    /**
     * Sends the bytes to the mobile.
     * 
     * @param b
     * @param len
     * @param offset
     * @return
     */
    public int writeBytes(byte[] b, int len, int offset) {
        log.debug("writing...");
        if(outp == null) {
            return -1;
        }
    
        int bytes_written = len;
        try {
            outp.write(b, offset, len);
            outp.flush();
            
            log.debug("wrote "+new String(b, offset, len));
        } catch (IOException e) {
                e.printStackTrace();
                return -1;
        }
        return bytes_written;
    }
    
    /**
     * Extracts the information of an answer.
     * 
     * @param b
     * @return
     */
    protected String getAnswer(byte[] b) {
        String answer = new String(b);
        String[] answers = answer.split("\r\n");
        
        if(answers.length < 1) {
            return answer;
        }
        
        return answers[1];
    }

}
